# 这是一个将你提供的BIO格式数据转换为指令格式的Python脚本。

import json
import re
from collections import defaultdict

def bio_to_entities(tokens, labels):
    """
    将BIO格式的token和label转换为实体字典
    """
    entities = defaultdict(list)
    current_entity = ""
    current_type = ""
    entity_tokens = []
    
    for token, label in zip(tokens, labels):
        if label == 'O':
            # 如果当前正在构建实体，先保存
            if current_entity:
                entities[current_type].append(current_entity)
                current_entity = ""
                current_type = ""
                entity_tokens = []
            continue
            
        # 处理B-和I-标签
        if label.startswith('B-'):
            # 如果当前正在构建实体，先保存
            if current_entity:
                entities[current_type].append(current_entity)
            
            # 开始新的实体
            entity_type = label[2:]  # 去掉"B-"前缀
            current_entity = token
            current_type = entity_type
            entity_tokens = [token]
            
        elif label.startswith('I-'):
            entity_type = label[2:]  # 去掉"I-"前缀
            
            # 检查是否与当前实体类型一致
            if current_type == entity_type and current_entity:
                current_entity += token
                entity_tokens.append(token)
            else:
                # 如果不一致，可能是标注错误，处理为新的实体开始
                if current_entity:
                    entities[current_type].append(current_entity)
                current_entity = token
                current_type = entity_type
                entity_tokens = [token]
    
    # 处理最后一个实体
    if current_entity:
        entities[current_type].append(current_entity)
    
    return dict(entities)

def parse_bio_data(bio_text):
    """
    解析BIO格式的文本数据
    """
    lines = bio_text.strip().split('\n')
    tokens = []
    labels = []
    
    for line in lines:
        if line.strip():  # 跳过空行
            parts = line.split()
            if len(parts) >= 2:
                tokens.append(parts[0])
                labels.append(parts[1])
    
    return tokens, labels

def create_instruction_data(tokens, entities, instruction_template=None):
    """
    创建指令数据
    """
    # 重建原始文本
    original_text = ''.join(tokens)
    
    # 默认指令模板
    if instruction_template is None:
        entity_types = list(entities.keys())
        if entity_types:
            instruction = f"请从给定的中医文本中识别出所有的{', '.join(entity_types)}实体，并以JSON格式输出。"
        else:
            instruction = "请从给定的中医文本中识别出所有的命名实体，并以JSON格式输出。"
    else:
        instruction = instruction_template
    
    # 创建输出
    output = json.dumps(entities, ensure_ascii=False, indent=2)
    
    return {
        "instruction": instruction,
        "input": original_text,
        "output": output
    }

def convert_bio_to_instruction(bio_text, instruction_template=None):
    """
    主函数：将BIO文本转换为指令数据
    """
    tokens, labels = parse_bio_data(bio_text)
    entities = bio_to_entities(tokens, labels)
    instruction_data = create_instruction_data(tokens, entities, instruction_template)
    
    return instruction_data

# 示例使用
if __name__ == "__main__":
    # 你提供的BIO数据
    bio_data = """现 O
头 O
昏 O
口 B-临床表现
苦 I-临床表现
目 O
的 O
观 O
察 O
复 B-中医治疗
方 I-中医治疗
丁 I-中医治疗
香 I-中医治疗
开 I-中医治疗
胃 I-中医治疗
贴 I-中医治疗
外 O
敷 O
神 O
阙 O
穴 O
治 O
疗 O
慢 O
性 O
心 B-西医诊断
功 I-西医诊断
能 I-西医诊断
不 I-西医诊断
全 I-西医诊断
伴 I-西医诊断
功 I-西医诊断
能 I-西医诊断
性 I-西医诊断
消 I-西医诊断
化 I-西医诊断
不 I-西医诊断
良 I-西医诊断
的 O
临 O
床 O
疗 O
效 O"""
    
    # 转换单个样本
    result = convert_bio_to_instruction(bio_data)
    print("单个样本转换结果:")
    print(json.dumps(result, ensure_ascii=False, indent=2))
    
    # 批量转换示例（如果你有多个BIO文件）
    def batch_convert_bio_files(bio_files, output_file):
        """
        批量转换多个BIO文件
        bio_files: BIO文件路径列表
        output_file: 输出的JSON文件路径
        """
        all_instruction_data = []
        
        for file_path in bio_files:
            with open(file_path, 'r', encoding='utf-8') as f:
                bio_text = f.read()
            
            instruction_data = convert_bio_to_instruction(bio_text)
            all_instruction_data.append(instruction_data)
        
        # 保存到文件
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(all_instruction_data, f, ensure_ascii=False, indent=2, separators=(',', ': '))
        
        print(f"成功转换 {len(all_instruction_data)} 个样本到 {output_file}")
    
    # 批量转换调用示例（取消注释使用）
    # bio_files = ['data1.bio', 'data2.bio', 'data3.bio']
    # batch_convert_bio_files(bio_files, 'instruction_data.json')
    
    # 创建多样化的指令模板
    instruction_templates = [
        "请从以下中医文本中提取所有的{entity_types}实体。",
        "作为一名中医文本分析专家，请识别出下文中的{entity_types}。",
        "请对以下中医文本进行命名实体识别，找出所有的{entity_types}实体。",
        "请分析以下中医文本，提取其中提到的{entity_types}信息。"
    ]
    
    # 使用不同指令模板创建多样本
    print("\n多样化指令示例:")
    tokens, labels = parse_bio_data(bio_data)
    entities = bio_to_entities(tokens, labels)
    entity_types = list(entities.keys())
    
    for i, template in enumerate(instruction_templates):
        custom_instruction = template.format(entity_types='、'.join(entity_types))
        diverse_data = create_instruction_data(tokens, entities, custom_instruction)
        print(f"\n示例 {i+1}:")
        print(f"指令: {diverse_data['instruction']}")
        print(f"输出: {diverse_data['output']}")


# 运行结果示例
{
  "instruction": "请从给定的中医文本中识别出所有的临床表现、中医治疗、西医诊断实体，并以JSON格式输出。",
  "input": "现头昏口苦目的观察复方丁香开胃贴外敷神阙穴治疗慢性心功能不全伴功能性消化不良的临床疗效",
  "output": {
    "临床表现": ["口苦"],
    "中医治疗": ["复方丁香开胃贴"],
    "西医诊断": ["心功能不全伴功能性消化不良"]
  }
}

# 如果你的BIO数据包含句子边界或更复杂的情况，可以使用这个增强版本：

def advanced_bio_conversion(bio_text_collection):
    """
    处理包含多个句子或文档边界的BIO数据集合
    """
    all_instruction_data = []
    
    # 按空行分割不同的句子/文档
    documents = re.split(r'\n\s*\n', bio_text_collection.strip())
    
    for doc_id, doc_text in enumerate(documents):
        if not doc_text.strip():
            continue
            
        lines = doc_text.strip().split('\n')
        tokens = []
        labels = []
        
        for line in lines:
            if line.strip():
                parts = line.split()
                if len(parts) >= 2:
                    tokens.append(parts[0])
                    labels.append(parts[1])
        
        if tokens:  # 确保不是空文档
            entities = bio_to_entities(tokens, labels)
            instruction_data = create_instruction_data(tokens, entities)
            instruction_data["doc_id"] = f"doc_{doc_id}"  # 添加文档ID
            all_instruction_data.append(instruction_data)
    
    return all_instruction_data

# 使用示例
multiple_bio_data = """
现 O
头 O
昏 O
口 B-临床表现
苦 I-临床表现

目 O
的 O
观 O
察 O
复 B-中医治疗
方 I-中医治疗
"""

results = advanced_bio_conversion(multiple_bio_data)
for result in results:
  print(json.dumps(result, ensure_ascii=False, indent=2))


# 批量处理: 如果你有多个BIO文件，使用batch_convert_bio_files函数

# 指令多样化: 使用不同的指令模板来增强模型的泛化能力

# 质量检查: 转换后手动检查一些样本，确保实体识别正确

# 数据分割: 将生成的数据按需分割为训练集、验证集和测试集

# 这个脚本可以很好地处理你提供的中医领域BIO数据，并生成适合大语言模型微调的指令数据格式。